/*!
 * \file WtUftStraDemo.cpp
 * \project WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief WonderTrader UFT超高频策略示例实现文件
 * 
 * \details 该文件实现了WtUftStraDemo类的完整功能，提供UFT超高频交易策略的完整示例：
 *          
 *          核心算法：
 *          1. 理论价格计算：
 *             理论价格 = (买一价×卖一量 + 卖一价×买一量) / (买一量 + 卖一量)
 *          2. 信号生成逻辑：
 *             - 理论价格 > 最新价：产生买入信号
 *             - 理论价格 < 最新价：产生卖出信号
 *          3. 交易执行：
 *             - FAK订单：Fill and Kill，立即成交否则撤销
 *             - 价格调整：在最新价基础上加减价格偏移
 *             - 频率控制：限制交易频率避免过度交易
 *          
 *          超高频特性：
 *          - 微秒级响应：极致优化的Tick数据处理
 *          - 智能撤单：基于时间的超时订单管理
 *          - 并发安全：SpinMutex保证多线程数据一致性
 *          - 通道监控：实时监控交易通道连接状态
 *          
 *          风险控制：
 *          - 持仓检查：确保交易方向与当前持仓一致
 *          - 频率限制：防止过度交易和市场冲击
 *          - 超时撤单：避免订单长时间挂单
 *          - 异常处理：完善的错误恢复机制
 *          
 *          性能优化：
 *          - 无锁编程：使用SpinMutex减少锁竞争
 *          - 内存管理：及时释放数据防止内存泄漏
 *          - 计算优化：缓存重复计算结果
 *          - 分支预测：优化条件判断逻辑
 */

#include "WtUftStraDemo.h"
#include "../Includes/IUftStraCtx.h"

#include "../Includes/WTSVariant.hpp"
#include "../Includes/WTSDataDef.hpp"
#include "../Includes/WTSContractInfo.hpp"
#include "../Share/TimeUtils.hpp"
#include "../Share/decimal.h"
#include "../Share/fmtlib.h"

extern const char* FACT_NAME;

/*!
 * \brief 构造函数
 * \param id 策略实例唯一标识符
 * 
 * \details 初始化超高频策略示例，设置默认参数：
 *          - 初始化成员变量为默认值
 *          - 设置最后入场时间为最大值（未入场状态）
 *          - 通道状态设为未就绪
 *          - 撤单计数器清零
 */
WtUftStraDemo::WtUftStraDemo(const char* id)
	: UftStrategy(id)
	, _last_tick(NULL)
	, _last_entry_time(UINT64_MAX)
	, _channel_ready(false)
	, _last_calc_time(0)
	, _lots(1)
	, _cancel_cnt(0)
{
}

/*!
 * \brief 析构函数
 * 
 * \details 清理资源，释放内存：
 *          - 释放缓存的Tick数据
 *          - 清理订单记录
 */
WtUftStraDemo::~WtUftStraDemo()
{
	if (_last_tick)
		_last_tick->release();
}

/*!
 * \brief 获取策略名称
 * \return 策略名称字符串
 */
const char* WtUftStraDemo::getName()
{
	return "UftDemoStrategy";
}

/*!
 * \brief 获取策略工厂名称
 * \return 工厂名称字符串
 */
const char* WtUftStraDemo::getFactName()
{
	return FACT_NAME;
}

/*!
 * \brief 初始化策略参数
 * \param cfg 配置参数对象
 * \return 初始化成功返回true，失败返回false
 * 
 * \details 从配置文件中读取策略参数：
 *          - code: 交易品种代码
 *          - second: 订单超时时间（秒）
 *          - freq: 交易频率限制（毫秒）
 *          - offset: 价格偏移量（tick数）
 *          - lots: 每次交易手数
 */
bool WtUftStraDemo::init(WTSVariant* cfg)
{
	// 这里演示一下外部配置参数的获取
	_code = cfg->getCString("code");
	_secs = cfg->getUInt32("second");
	_freq = cfg->getUInt32("freq");
	_offset = cfg->getUInt32("offset");

	_lots = cfg->getDouble("lots");

	return true;
}

/*!
 * \brief 订单委托结果事件处理
 * \param localid 本地订单ID
 * \param bSuccess 委托是否成功
 * \param message 错误消息
 * 
 * \details 处理订单委托结果：
 *          - 委托失败时从订单集合中移除该订单
 *          - 避免无效订单占用内存和处理时间
 */
void WtUftStraDemo::on_entrust(uint32_t localid, bool bSuccess, const char* message)
{
	if(!bSuccess)
	{
		auto it = _orders.find(localid);
		if(it != _orders.end())
			_orders.erase(it);
	}
}

/*!
 * \brief 策略初始化事件
 * \param ctx 策略上下文
 * 
 * \details 策略启动时调用：
 *          - 订阅指定品种的Tick数据
 *          - 获取历史K线数据进行预热
 *          - 保存策略上下文引用
 */
void WtUftStraDemo::on_init(IUftStraCtx* ctx)
{
	//WTSTickSlice* ticks = ctx->stra_get_ticks(_code.c_str(), _count);
	//if (ticks)
	//	ticks->release();

	WTSKlineSlice* kline = ctx->stra_get_bars(_code.c_str(), "m1", 30);
	if (kline)
		kline->release();

	ctx->stra_sub_ticks(_code.c_str());

	_ctx = ctx;
}

/*!
 * \brief Tick数据事件处理
 * \param ctx 策略上下文
 * \param code 品种代码
 * \param newTick 新的Tick数据
 * 
 * \details 处理实时Tick数据，这是超高频策略的核心逻辑：
 *          
 *          处理流程：
 *          1. 品种过滤：只处理配置的交易品种
 *          2. 订单检查：如有活跃订单则检查超时撤单
 *          3. 通道验证：确保交易通道已就绪
 *          4. 频率控制：避免过度交易
 *          5. 信号计算：基于理论价格生成交易信号
 *          6. 订单执行：根据信号和持仓情况执行交易
 *          
 *          理论价格算法：
 *          理论价格 = (买一价×卖一量 + 卖一价×买一量) / (买一量 + 卖一量)
 *          
 *          交易逻辑：
 *          - 理论价格 > 最新价 且 当前持仓 <= 0：买入开仓
 *          - 理论价格 < 最新价 且 当前持仓 >= 0：卖出开仓
 */
void WtUftStraDemo::on_tick(IUftStraCtx* ctx, const char* code, WTSTickData* newTick)
{	
	if (_code.compare(code) != 0)
		return;

	if (!_orders.empty())
	{
		check_orders();
		return;
	}

	if (!_channel_ready)
		return;

	WTSTickData* curTick = ctx->stra_get_last_tick(code);
	if (curTick)
		curTick->release();

	uint32_t curMin = newTick->actiontime() / 100000;	// actiontime是带毫秒的，要取得分钟，所以要除以10万
	if (curMin > _last_calc_time)
	{// 如果spread上次计算时间小于当前分钟，重算spread
		//WTSKlineSlice* kline = ctx->stra_get_bars(code, "m5", 30);
		//if (kline)
		//	kline->release();

		// 重新计算以后，更新记录时间
		_last_calc_time = curMin;
	}

	// 指定秒数内不重复入场
	uint64_t now = TimeUtils::makeTime(ctx->stra_get_date(), ctx->stra_get_time() * 100000 + ctx->stra_get_secs());
	if(now - _last_entry_time <= _freq * 1000)
	{
		return;
	}

	int32_t signal = 0;
	double price = newTick->price();
	// 计算部分：理论价格计算
	double pxInThry = (newTick->bidprice(0)*newTick->askqty(0) + newTick->askprice(0)*newTick->bidqty(0)) / (newTick->bidqty(0) + newTick->askqty(0));

	// 最新价格和理论价格比较
	if (pxInThry > price)
	{
		// 买入信号
		signal = 1;
	}
	else if (pxInThry < price)
	{
		// 卖出信号
		signal = -1;
	}

	if (signal != 0)
	{
		double curPos = ctx->stra_get_position(code);

		WTSCommodityInfo* cInfo = ctx->stra_get_comminfo(code);

		if(signal > 0  && decimal::le(curPos, 0))
		{// 买入信号，且当前持仓小于等于0
			// 最新价+2个最小价位下单
			double targetPx = price + cInfo->getPriceTick() * _offset;

			auto ids = ctx->stra_buy(code, targetPx, _lots, UFT_OrderFlag_FAK);

			{
				_mtx_ords.lock();
				for (uint32_t localid : ids)
					_orders.insert(localid);
				_mtx_ords.unlock();
				_last_entry_time = now;
			}
			
		}
		else if (signal < 0 && decimal::ge(curPos, 0))
		{// 卖出信号，且当前持仓大于0，或者持仓为0但不是股票，或者持仓为0但是期货可以建空仓
			// 最新价-2个最小价位下单
			double targetPx = price - cInfo->getPriceTick()*_offset;

			auto ids = ctx->stra_sell(code, targetPx, _lots, UFT_OrderFlag_FAK);

			{
				_mtx_ords.lock();
				for (uint32_t localid : ids)
					_orders.insert(localid);
				_mtx_ords.unlock();
				_last_entry_time = now;
			}
		}
	}
}

/*!
 * \brief 检查订单状态
 * 
 * \details 检查所有活跃订单的状态：
 *          - 检查订单是否超时
 *          - 超时订单自动撤销
 *          - 更新撤单统计
 *          
 *          超时逻辑：
 *          - 订单下达后超过配置的秒数未成交
 *          - 自动撤销所有相关订单
 *          - 记录撤单次数用于策略分析
 */
void WtUftStraDemo::check_orders()
{
	if (!_orders.empty() && _last_entry_time != UINT64_MAX)
	{
		uint64_t now = TimeUtils::makeTime(_ctx->stra_get_date(), _ctx->stra_get_time() * 100000 + _ctx->stra_get_secs());
		if (now - _last_entry_time >= _secs * 1000)	// 如果超过一定时间没有成交的，撤单
		{
			_mtx_ords.lock();
			for (auto localid : _orders)
			{
				_ctx->stra_cancel(localid);
				_cancel_cnt++;
				_ctx->stra_log_info(fmt::format("Order expired, cancelcnt updated to {}", _cancel_cnt).c_str());
			}
			_mtx_ords.unlock();
		}
	}
}

/*!
 * \brief K线数据事件处理
 * \param ctx 策略上下文
 * \param code 品种代码
 * \param period K线周期
 * \param times K线次数
 * \param newBar 新的K线数据
 * 
 * \details 处理K线数据更新事件：
 *          - 当前示例策略主要基于Tick数据
 *          - K线事件可用于长期趋势分析
 *          - 可扩展实现基于K线的信号过滤
 */
void WtUftStraDemo::on_bar(IUftStraCtx* ctx, const char* code, const char* period, uint32_t times, WTSBarStruct* newBar)
{
	// 超高频策略主要基于Tick数据，K线数据处理可根据需要扩展
}

/*!
 * \brief 交易成交事件处理
 * \param ctx 策略上下文
 * \param localid 本地订单ID
 * \param stdCode 标准合约代码
 * \param isLong 是否为多头方向
 * \param offset 开平仓标志
 * \param qty 成交数量
 * \param price 成交价格
 * 
 * \details 处理订单成交事件：
 *          - 更新策略内部状态
 *          - 记录成交信息
 *          - 可扩展实现成交后的风险控制
 */
void WtUftStraDemo::on_trade(IUftStraCtx* ctx, uint32_t localid, const char* stdCode, bool isLong, uint32_t offset, double qty, double price)
{
	// 成交事件处理逻辑可根据需要扩展
}

/*!
 * \brief 持仓变化事件处理
 * \param ctx 策略上下文
 * \param stdCode 标准合约代码
 * \param isLong 是否为多头方向
 * \param prevol 变化前总持仓
 * \param preavail 变化前可用持仓
 * \param newvol 变化后总持仓
 * \param newavail 变化后可用持仓
 * 
 * \details 处理持仓变化事件：
 *          - 更新策略内部持仓记录
 *          - 记录持仓变化日志
 *          - 用于风险控制和状态监控
 */
void WtUftStraDemo::on_position(IUftStraCtx* ctx, const char* stdCode, bool isLong, double prevol, double preavail, double newvol, double newavail)
{
	if (_code != stdCode)
		return;

	_prev = prevol;
	_ctx->stra_log_info(fmt::format("There are {} of {} before today", _prev, stdCode).c_str());
}

/*!
 * \brief 订单状态变化事件处理
 * \param ctx 策略上下文
 * \param localid 本地订单ID
 * \param stdCode 标准合约代码
 * \param isLong 是否为多头方向
 * \param offset 开平仓标志
 * \param totalQty 订单总数量
 * \param leftQty 剩余数量
 * \param price 订单价格
 * \param isCanceled 是否已撤销
 * 
 * \details 处理订单状态变化事件：
 *          - 跟踪订单生命周期
 *          - 管理活跃订单集合
 *          - 更新撤单统计
 *          - 订单完成或撤销时清理记录
 */
void WtUftStraDemo::on_order(IUftStraCtx* ctx, uint32_t localid, const char* stdCode, bool isLong, uint32_t offset, double totalQty, double leftQty, double price, bool isCanceled)
{
	// 不是我们发出去的订单，就不处理
	auto it = _orders.find(localid);
	if (it == _orders.end())
		return;

	// 如果已撤销，或者剩余数量为0，就清除原有的id记录
	if(isCanceled || leftQty == 0)
	{
		_mtx_ords.lock();
		_orders.erase(it);
		if (_cancel_cnt > 0)
		{
			_cancel_cnt--;
			_ctx->stra_log_info(fmt::format("cancelcnt -> {}", _cancel_cnt).c_str());
		}
		_mtx_ords.unlock();
	}
}

/*!
 * \brief 交易通道就绪事件处理
 * \param ctx 策略上下文
 * 
 * \details 当交易通道连接成功时调用：
 *          - 检查是否有未完成订单需要处理
 *          - 撤销可能存在的历史订单
 *          - 设置通道就绪标志
 *          - 恢复策略正常运行
 */
void WtUftStraDemo::on_channel_ready(IUftStraCtx* ctx)
{
	double undone = _ctx->stra_get_undone(_code.c_str());
	if (!decimal::eq(undone, 0) && _orders.empty())
	{
		// 这说明有未完成的订单在策略之外，先撤掉
		_ctx->stra_log_info(fmt::format("{}中存在策略外的未完成订单 {} 手，全部撤销", _code, undone).c_str());

		OrderIDs ids = _ctx->stra_cancel_all(_code.c_str());
		for (auto localid : ids)
		{
			_orders.insert(localid);
		}
		_cancel_cnt += ids.size();

		_ctx->stra_log_info(fmt::format("cancelcnt -> {}", _cancel_cnt).c_str());
	}

	_channel_ready = true;
}

/*!
 * \brief 交易通道断开事件处理
 * \param ctx 策略上下文
 * 
 * \details 当交易通道连接断开时调用：
 *          - 设置通道状态为未就绪
 *          - 暂停新订单的发送
 *          - 保护策略状态
 */
void WtUftStraDemo::on_channel_lost(IUftStraCtx* ctx)
{
	_channel_ready = false;
}